use std::sync::{Arc, Mutex};

use chardev::CharBackendMap;
use dfs::{
    dfs_read,
    inode::{inode_root, link_path_walk, DirCreateOptions},
    params::ParamCreateOptions,
    seq_file::SeqFileOperation,
    seq_println,
};
use manage::{ManageGuestStatus, SiminkManage};
use monitor::{Monitor, ReadlinePrompt};

mod chardevice_mon;
mod protocol;
mod stdio;
mod telnet;

use self::{protocol::protocol_process, stdio::stdio_init, telnet::telnet_init};
use crate::cli::SiminkCli;

pub(crate) fn monitor_init(mon: Arc<Monitor>, manage: Arc<SiminkManage>, arg: &SiminkCli) {
    let mon_inode =
        DirCreateOptions::new().read_write(true).name("monitor").create_dir(inode_root()).unwrap();

    stdio_init(mon.clone(), manage.clone(), mon_inode.clone());
    telnet_init(mon.clone(), manage.clone());

    // 如果启用控制协议, 启动控制协议
    if arg.protocol() {
        protocol_process(mon.clone(), manage.clone(), arg.tcp());
        ParamCreateOptions::new()
            .read(true)
            .name("protocol")
            .param_string(mon_inode, Arc::new(Mutex::new(arg.tcp())))
            .unwrap();
    }

    // 如果需要进入命令行监视器, 则启动命令行监视器
    if arg.interactive() {
        manage.notify_readline_run();
    }
}

#[derive(Debug, Default)]
pub(crate) struct SiminkMonitorPrompt;

impl ReadlinePrompt for SiminkMonitorPrompt {
    fn get_indicator_color(&self) -> monitor::MonColor {
        monitor::MonColor::DarkBlue
    }
    fn get_prompt_left_color(&self) -> monitor::MonColor {
        monitor::MonColor::DarkBlue
    }

    fn get_prompt_right_color(&self) -> monitor::MonColor {
        let mut buf = String::new();
        let _ = dfs_read("/board/status", &mut buf);
        if buf == ManageGuestStatus::GuestRunning.name() {
            monitor::MonColor::Green
        } else {
            monitor::MonColor::Red
        }
    }

    fn render_prompt_indicator(&self) -> std::borrow::Cow<str> {
        std::borrow::Cow::Owned("$ ".into())
    }

    fn render_prompt_left(&self) -> std::borrow::Cow<str> {
        let mut buf = String::new();
        let res = dfs_read("/board/current_board", &mut buf);
        let default =
            || std::borrow::Cow::Owned(format!("[simink@{}]", link_path_walk(".").unwrap().name()));
        if res.is_err() || buf.is_empty() {
            default()
        } else {
            let name = buf.split(':').collect::<Vec<&str>>();
            if name.len() != 2 {
                default()
            } else {
                std::borrow::Cow::Owned(format!(
                    "[{}@{} {}]",
                    name[1],
                    name[0],
                    link_path_walk(".").unwrap().name()
                ))
            }
        }
    }

    fn render_prompt_right(&self) -> std::borrow::Cow<str> {
        let mut buf = String::new();
        let res = dfs_read("/board/status", &mut buf);
        if res.is_err() {
            std::borrow::Cow::Owned("No board".into())
        } else {
            std::borrow::Cow::Owned(buf)
        }
    }
}

pub(crate) struct MonitorFile {
    map: Arc<CharBackendMap>,
}

impl SeqFileOperation for MonitorFile {
    fn show(
        &self,
        m: &mut dfs::seq_file::SeqFile,
        _: &mut Box<dyn std::any::Any + Send>,
    ) -> dfs::DfsResult<()> {
        self.map.for_each(|c, is_current| {
            seq_println!(m, "{} {}", if is_current { "*" } else { " " }, c);
        });
        Ok(())
    }

    fn write(&self, buf: &str, pos: &mut u64) -> dfs::DfsResult<usize> {
        self.map.select_current(buf).ok_or(dfs::DfsError::InvalidArgs)?;
        *pos = 0;
        Ok(buf.len())
    }
}
